
import ByteStream from "./ByteStream";

function bitsToNum(bitArray){
	return bitArray.reduce(function(s, n) { return s * 2 + n; }, 0);
}

export default class DataParser {
    
    constructor(data) {
        this.stream = new ByteStream(data);
        // the final parsed object from the data
        this.output = {};
    }

    parse(schema) {
        this.parseParts(this.output, schema);	
        return this.output;
    }

    parseParts(obj, schema) {
        for(var i=0; i<schema.length; i++){
            var part = schema[i];
            this.parsePart(obj, part); 
        }
    }

    parsePart(obj, part){
        var name = part.label;
        var value;
    
        // make sure the part meets any parse requirements
        if(part.requires && ! part.requires(this.stream, this.output, obj)){
            return;
        }
        
        if(part.loop){
            // create a parse loop over the parts
            var items = [];
            while(part.loop(this.stream)){
                var item = {};
                this.parseParts(item, part.parts);
                items.push(item);
            }
            obj[name] = items;
        }else if(part.parts){
            // process any child parts
            value = {};
            this.parseParts(value, part.parts);
            obj[name] = value;
        }else if(part.parser){
            // parse the value using a parser
            value = part.parser(this.stream, this.output, obj);
            if(!part.skip){
                obj[name] = value;
            }
        }else if(part.bits){
            // convert the next byte to a set of bit fields
            obj[name] = this.parseBits(part.bits);
        }
    }

    parseBits(details) {
        var out = {};
        var bits = this.stream.readBitArray();
        for(var key in details){
            var item = details[key];
            if(item.length){
                // convert the bit set to value
                out[key] = bitsToNum(bits.slice(item.index, item.index + item.length));
            }else{
                out[key] = bits[item.index];
            }
        }
        return out;
    }
}
